{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 基础操作\n",
    "\n",
    "这里我们可以讨论多数SymPy对于表达式的基础操作。一些更高级的操作会在后面的章节讨论。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sympy import *\n",
    "x, y, z = symbols(\"x y z\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 代换\n",
    "\n",
    "一个最常见的应用就是在数学表达式中进行代换。代换替换了所有某类表达式的实例，通过`subs`方法，例如: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "cos(y) + 1"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "expr = cos(x) + 1\n",
    "expr.subs(x, y)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "代换通常会由于下面两个原因中的一个被使用: \n",
    "\n",
    "1. 代换一个表达式在某个点。例如，如果表达式是`cos(x) + 1`并且我们需要计算它在`x=0`，所以我们应该获得`cos(0) + 1`的值是`2`。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "expr.subs(x, 0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "2. 替换一个子表达式为另外一个表达式。这里通常有两个原因。第一个是如果我们要创建表达式其中包含对称，诸如x的x次方的x次方。要构建此类表达式，我们需要始于`x**y`，如果替换`y`为新的表达式`x**x`，就可以获得`x**(x**(x**x))`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "x**y"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "expr = x**y\n",
    "expr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "x**(x**y)"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "expr = expr.subs(y, x**y)\n",
    "expr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "x**(x**(x**x))"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "expr = expr.subs(y, x**x)\n",
    "expr"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "第二个如果我们要执行一个非常可控的化简，或者SymPy无法做到的化简。例如，假如有`sin(2x) + cos(2x)`，我们想用`2sin(x)cos(x)`替换`sin(2x)`。后面我们将学习，函数expand_trig可以做到这一点。但是，此函数也会扩展`cos(2x)`，这可能是我们不想要的。虽然有多种方法可以执行这种精确的简化，但我们将在高级表达式操作部分学习其中的一些方法，但一种简单的方法是仅将`sin(2x)`替换为`2sin(x)cos(x)`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2*sin(x)*cos(x) + 2*cos(x)**2 - 1"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "expr = sin(2*x) + cos(2*x)\n",
    "expand_trig(expr)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2*sin(x)*cos(x) + cos(2*x)"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "expr.subs(sin(2*x), 2*sin(x)*cos(x))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "有两件重要的事情需要注意关于`subs`。首先，它返回一个新的表达式。SymPy对象是不可变的。这意味着`subs`不会改变表达式。例如: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "expr = cos(x)\n",
    "expr.subs(x, 0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "cos(x)"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "expr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "x"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这里我们看到执行`expr.subs(x, 0)`保持`expr`未改变。事实上，SymPy表达式是不可变的，没有函数会原地改变这些表达式。所有的函数都会返回新的表达式。要同时触发多个置换操作，通过一组`(old, new)`对的`subs`。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "40"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "expr = x**3 + 4*x*y - z\n",
    "expr.subs([(x, 2), (y, 4), (z, 0)])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "通常将此功能与列表理解功能结合使用，一次可以完成大量相似的替换操作。 例如，假设我们有x4−4x3 + 4x2−2x + 3，我们想用y替换所有具有偶数幂的x实例，以获得y4−4x3 + 4y2−2x + 3。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "-4*x**3 - 2*x + y**4 + 4*y**2 + 3"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "expr = x**4 - 4*x**3 + 4*x**2 -2*x + 3\n",
    "replacements = [(x**i, y**i) for i in range(5) if i%2 == 0]\n",
    "expr.subs(replacements)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 转换字符串为SymPy表达式\n",
    "\n",
    "`sympify`函数，可以用来转换字符串为SymPy表达式。注意, `sympify`使用`eval`。不要将它用于未消毒的输入。例如: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "x**2 + 3*x - 1/2"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "str_expr = \"x**2 + 3*x - 1/2\"\n",
    "expr = sympify(str_expr)\n",
    "expr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "19/2"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "expr.subs(x, 2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### evalf\n",
    "\n",
    "要将数值表达式计算为浮点数，请使用evalf。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2.82842712474619"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "expr = sqrt(8)\n",
    "expr.evalf()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "SymPy可以计算浮点数表达式为任意精度。默认，15位精度，但可以为`evalf`传递任意参数。下面计算π的前100位。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117068"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pi.evalf(100)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "为了在某个点为符号表达式进行数值求值，我们可能会在`evalf`之后使用`subs`,但是更加精确有效的办法是使用`subs`传入代换到`evalf`，通过关于`Symbol:point`的字典对。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.0874989834394464"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "expr = cos(2*x)\n",
    "expr.evalf(subs={x:2.4})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "有时，舍入误差小于计算表达式后剩余的所需精度。 可以根据用户的意愿通过将“ chop”标志设置为“ True”来删除此类数字。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "-0.e-124"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "one = cos(1)**2 + sin(1)**2\n",
    "(one-1).evalf()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "(one - 1).evalf(chop = True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### lambdify\n",
    "\n",
    "如果您想进行简单的求值，则subs和evalf很好，但是如果您打算在多个点上求一个表达式，则有更有效的方法。 例如，如果您想对一千个点求值，那么使用SymPy的速度将远远慢于所需的速度，尤其是在仅关心机器精度的情况下。 相反，应该使用NumPy和SciPy之类的库。\n",
    "\n",
    "将SymPy表达式转换为可以进行数值评估的表达式的最简单方法是使用lambdify函数。 lambdify的行为类似于lambda函数，只是它将SymPy名称转换为给定数字库（通常为NumPy）的名称。注意: `lambdify`使用了`eval`，不要将其用于未消毒的输入。 例如"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 0.        ,  0.84147098,  0.90929743,  0.14112001, -0.7568025 ,\n",
       "       -0.95892427, -0.2794155 ,  0.6569866 ,  0.98935825,  0.41211849])"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import numpy\n",
    "a = numpy.arange(10)\n",
    "expr = sin(x)\n",
    "f = lambdify(x, expr, \"numpy\")\n",
    "f(a)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "也可以使用其他类似numpy的库。例如，使用标准库math模块，使用\"math\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.09983341664682815"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "f = lambdify(x, expr, \"math\")\n",
    "f(0.1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "要将`lambdify`与它不知道的数值库一起使用，输入`sympy_name:numerical_function`字典对。例如: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.1"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def mysin(x):\n",
    "    \"\"\"\n",
    "    My sine. Note that this is only accurate for small x.\n",
    "    \"\"\"\n",
    "    return x\n",
    "f = lambdify(x, expr, {\"sin\":mysin})\n",
    "f(0.1)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
